﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace StateMachine
{
	public class TuringMachine
	{
		public int MaxSteps { get; set; }
		public StateCollection States { get; set; }
		public HashSet<string> Alphabet { get; set; }
		//public IEnumerable<Tape> Tapes { get; set; }
		public Tape Tape { get; set; }
		public State StartState { get; set; }
		public State EndState { get; set; }
		public NoteCollection Notes { get; set; }

		public State CurrentState { get; private set; }

		public TuringMachine()
		{
			MaxSteps = 10000;
			Alphabet = new HashSet<string>();
			Tape = new Tape();
			States = new StateCollection();
			Notes = new NoteCollection();
		}

		public bool ValidateMachine()
		{
			return StartState != null && EndState != null
				&& States.All(state => state.Transitions.Select(t => t.Read).Distinct().Count() == state.Transitions.Count());
		}

		/// <summary>
		/// Run a given input string through the machine
		/// </summary>
		/// <param name="tape">The input to put into the machine</param>
		/// <returns>True if the input was accepted by the machine, otherwise false</returns>
		public bool Run(string tape)
		{
			Initialize(tape);
			int transitionCount = 0;

			while (!CurrentState.IsFinal)
			{
				Console.WriteLine(transitionCount);
				if (!Step() || transitionCount > MaxSteps)
				{
					
					return false;
				}
				transitionCount++;
			}

			return true;
		}

		
		public bool[] Run(string[] tapes)
		{
			
			bool[] result = new bool[tapes.Length];
			for (int i = 0; i < tapes.Length; i++)
			{
				result[i] = Run(tapes[i]);
			}
			return result;
		}

		/// <summary>
		/// Setup a given input string for the turing machine
		/// </summary>
		/// <param name="tape">The input to put onto the tape</param>
		public void Initialize(string tape)
		{
			if (!ValidateMachine())
				throw new InvalidMachineException();
			CurrentState = StartState;
			Tape.LoadString(tape);
		}

		/// <summary>
		/// Perform one step with a given input string
		/// </summary>
		/// <returns>True if the a transition was made successfully to a new state, otherwise false</returns>
		public bool Step()
		{
			List<Transition> transitions = CurrentState.Transitions.GetTransitionsFor(Tape.Current);
			if (transitions.Count == 1)
			{
				Tape.ApplyTransition(transitions[0]);
				CurrentState = transitions[0].EndState;
				return true;
			}
			return false;
		}

		public override string ToString()
		{
			//start // end // states
			StringBuilder result = new StringBuilder();
			result.AppendLine("Start: " + StartState.ToString());
			result.AppendLine("End: " + EndState.ToString());
			result.AppendLine("Current: " + CurrentState ?? " Null");
			for (int i = 0; i < States.Count; i++)
			{
				result.AppendLine((i + 1) + ". \n" + States[i]);
			}
			result.AppendLine("Tape: " + Tape.ToString());

			return result.ToString();
		}
	}

	public class InvalidMachineException : Exception
	{
		public InvalidMachineException()
		{

		}
		public InvalidMachineException(string Message)
			: base(Message)
		{

		}
	}

}